var async = require("async");
var fs = require("fs");
var path = require("path");

var crypto = require('crypto');

function combineOptions(options) {
  var result = {};
  var defaultOptions = {
    algorithm: 'aes-128-ecb' // aes192
  };
  
  for (var key in defaultOptions) {
    result[key] = defaultOptions[key];
  }
  
  for (var key in options) {
    result[key] = options[key];
  }
  return result;
};


// cursively make dir
function mkdirs(p, mode, f, made) {
    if (typeof mode === 'function' || mode === undefined) {
        f = mode;
        mode = 0777 & (~process.umask());
    }
    if (!made)
        made = null;
  
    var cb = f || function () {};
    if (typeof mode === 'string')
        mode = parseInt(mode, 8);
    p = path.resolve(p);
  
    fs.mkdir(p, mode, function (er) {
        if (!er) {
            made = made || p;
            return cb(null, made);
        }
        switch (er.code) {
        case 'ENOENT':
            mkdirs(path.dirname(p), mode, function (er, made) {
                if (er) {
                    cb(er, made);
                } else {
                    mkdirs(p, mode, cb, made);
                }
            });
            break;
  
            // In the case of any other error, just see if there's a dir
            // there already.  If so, then hooray!  If not, then something
            // is borked.
        default:
            fs.stat(p, function (er2, stat) {
                // if the stat fails, then that's super weird.
                // let the original error be the failure reason.
                if (er2 || !stat.isDirectory()) {
                    cb(er, made);
                } else {
                    cb(null, made)
                };
            });
            break;
        }
    });
}
// single file copy
function copyFile(file, toDir, passwordKey, cb) {
    async.waterfall([
            function (callback) {
                fs.exists(toDir, function (exists) {
                    if (exists) {
                        callback(null, false);
                    } else {
                        callback(null, true);
                    }
                });
            }, function (need, callback) {
                if (need) {
                    mkdirs(path.dirname(toDir), callback);
                } else {
                    callback(null, true);
                }
            }, function (p, callback) {
                
                // var reads = fs.createReadStream(file);
                // var writes = fs.createWriteStream(path.join(path.dirname(toDir), path.basename(file)));
                var options = {};
                options = combineOptions(options)
                
                var infile = path.join(path.dirname(toDir), path.basename(file))
                var inputStream = fs.createReadStream(file);
                var outputStream = fs.createWriteStream(infile);
                
                var keyBuf = new Buffer(passwordKey);

                var cipher = crypto.createCipher(options.algorithm, keyBuf);
                
                inputStream.on("error", function(err) {
                  callback(true, err);
                });
                
                inputStream.on('data', function(data) {
                  var buf = new Buffer(cipher.update(data), 'binary');
                  outputStream.write(buf);
                });
                
                inputStream.on('end', function() {
                  var buf = new Buffer(cipher.final('binary'), 'binary');
                  console.log('\t正在加密:' + infile);
                  outputStream.write(buf);
                  outputStream.end();
                  outputStream.on('close', function() {
                    callback(null);
                  });
                });
            }
        ], cb);
  
}
  
// cursively count the  files that need to be copied
  
function _ccoutTask(from, to, cbw) {
    async.waterfall([
            function (callback) {
                fs.stat(from, callback);
            },
            function (stats, callback) {
                if (stats.isFile()) {
                    cbw.addFile(from, to);
                    callback(null, []);
                } else if (stats.isDirectory()) {
                    fs.readdir(from, callback);
                }
            },
            function (files, callback) {
                if (files.length) {
                    for (var i = 0; i < files.length; i++) {
                        _ccoutTask(path.join(from, files[i]), path.join(to, files[i]), cbw.increase());
                    }
                }
                callback(null);
            }
        ], cbw);
  
}
// wrap the callback before counting
function ccoutTask(from, to, cb) {
    var files = [];
    var count = 1;
  
    function wrapper(err) {
        count--;
        if (err || count <= 0) {
            cb(err, files)
        }
    }
    wrapper.increase = function () {
        count++;
        return wrapper;
    }
    wrapper.addFile = function (file, dir) {
        files.push({
            file : file,
            dir : dir
        });
    }
  
    _ccoutTask(from, to, wrapper);
}
  
  
function encryptDir(from, to,passwordKey, cb) {
    if(!cb){
      cb=function(){};
    }
    async.waterfall([
            function (callback) {
                fs.exists(from, function (exists) {
                    if (exists) {
                        callback(null, true);
                    } else {
                        console.log(from + " not exists");
                        callback(true);
                    }
                });
            },
            function (exists, callback) {
                fs.stat(from, callback);
            },
            function (stats, callback) {
                if (stats.isFile()) {
                    // one file copy
                    copyFile(from, to, function (err) {
                        if (err) {
                            // break the waterfall
                            callback(true);
                        } else {
                            callback(null, []);
                        }
                    });
                } else if (stats.isDirectory()) {
                    ccoutTask(from, to, callback);
                }
            },
            function (files, callback) {
                // prevent reaching to max file open limit
                async.mapLimit(files, 10, function (f, cb) {
                    copyFile(f.file, f.dir, passwordKey, cb);
                }, callback);
            }
        ], cb);
}
  




var start = new Date().getTime();
encryptDir("/Users/comeontom/Desktop/html5", "/Users/comeontom/Desktop/html52","123456", function (err) {
    if (err) {
        console.log("error ocur");
        console.dir(err);
    } else {
  
        console.log("copy ok");
        console.log("consume time:" + (new Date().getTime() - start))
    }
});
